* Stata function that tests Zero Conditional ATE, Constant Conditional ATE, and Zero ATE hypotheses
* as in Crump, R. K.,V. J. Hotz, G. W. Imbens and O. A. Mitnik (2008) "Nonaparametric Tests for Treatment Effect Heterogeneity",
* Review of Economics and Statistics, August, 90(3): 389-405.

* See help file for details.

* This version: December 1, 2008

program test_condate, rclass
	version 9.2
	syntax varlist(min=2)                           ///
		  [in] [if] 						   ///
		  [pweight aweight iweight fweight],       ///
		  tvar(varname)                            ///
		  [                                        ///
		  NORobust                                 ///
		  ]

	* Assigns macro variables containing outcome variable and conditioning variables
	tokenize `varlist'
     local outvar `1'
     macro shift
     local condvars `*'

	* Updates macro `if'
	if "`if'"=="" local ifcond "if "
	else local ifcond "`if' & "
	
	* Macro wgt_exp
	if "`weight'"~="" local wgt_exp "[`weight' `exp']"
		
	* Checks that tvar is a dummy variable
	qui tab `tvar', matrow(tvals)
	if rowsof(tvals)~=2 | tvals[1,1]~=0 | tvals[2,1]~=1 {
		di as err ""
		di as err "Treatment variable invalid, it has to be a dummy variable 0,1"
		exit 
	}

	* Makes sure that the same variables that are used in estimating the regression function
	* for controls, are used for treated 
	qui _rmcoll `condvars' `in' `ifcond' `tvar'==0 `wgt_exp'
	local nocolvars0 = r(varlist)
	qui _rmcoll `condvars' `in' `ifcond' `tvar'==1 `wgt_exp'
	local nocolvars1 = r(varlist)
	local nocolvars: list nocolvars0 & nocolvars1
	local K: list sizeof nocolvars
	
	* Determines whethere it will use robust var-cov matrix or not
	if "`norobust'"=="" {
		local rob "robust"
		local robtit "Calculated using heteroskedastic-robust Var-Cov matrix"
	}
	else {
		local rob ""
		local robtit "Calculated using homoskedastic Var-Cov matrix"
	}
	
	* Regression functions
	tempname b0 b1 v0 v1
	* Controls
	qui regress `outvar' `nocolvars' `in' `ifcond' `tvar'==0 `wgt_exp', `rob'
	matrix `b0'=e(b)'
	matrix `v0'=e(V)
	* Treated
	qui regress `outvar' `nocolvars' `in' `ifcond' `tvar'==1 `wgt_exp', `rob'
	matrix `b1'=e(b)'
	matrix `v1'=e(V)


	* Calculate Q test for zero conditional ATE hypothesis
	tempname Vhat1 Qzcte Tzcte
	matrix `Vhat1'=`v0'+`v1'
	matrix `Qzcte'=(`b1'-`b0')'*invsym(`Vhat1')*(`b1'-`b0')
	local dfQzcte=`K'+1  // dof
	local pQzcte=1-chi2(`dfQzcte',`Qzcte'[1,1]) //p-value

	* Calculate T test for zero conditional ATE hypothesis
	matrix `Tzcte'=(`Qzcte'-(`K'+1))/sqrt(2*(`K'+1)) // statistic
	local pTzcte=1-normal(`Tzcte'[1,1])  // p-value

	* Calculate Q test for constant conditional ATE hypothesis
	tempname Vhat2 Qccte Tccte
	matrix `Vhat2'=`v0'[1..`K',1..`K']+`v1'[1..`K',1..`K'] // Doest not take elements corresponding to _cons (in position K+1)
	matrix `Qccte'=(`b1'[1..`K',1]-`b0'[1..`K',1])'*invsym(`Vhat2')*(`b1'[1..`K',1]-`b0'[1..`K',1])  // statistic
	local dfQccte =`K' // dof
	local pQccte=1-chi2(`dfQccte',`Qccte'[1,1]) // p-value
	
	* Calculate T test for constant conditional ATE hypothesis
	matrix `Tccte'=(`Qccte'-`K')/sqrt(2*`K') // statistic
	local pTccte=1-normal(abs(`Tccte'[1,1])) // p-value (1-sided)

	* Calculate T test for zero ATE (difference of means)
	tempname Tzate Qzate
	qui ttest  `outvar' `if' `in', by(`tvar') unpaired unequal
	matrix `Tzate'=-r(t)
	local pTzate=r(p)
	matrix `Qzate'=`Tzate'[1,1]^2
	local pQzate=1-chi2(1,`Qzate'[1,1]) // p-value

	* Collect results (all are 1x3 row vectors with 1st, 2nd and 3rd column representing the 
	* corresponding result for the zero conditional ATE, constant conditional ATE and zero ATE null hypotheses in this order
	tempname Qtests dfQtests pQtests Ttests pTtests results_display
	matrix `Qtests'=(`Qzcte',`Qccte',`Qzate')
	matrix `dfQtests'=(`dfQzcte',`dfQccte',1)
	matrix `pQtests'=(`pQzcte',`pQccte',`pQzate')
	matrix `Ttests'=(`Tzcte',`Tccte',`Tzate')
	matrix `pTtests'=(`pTzcte',`pTccte',`pTzate')
	
	* Matrix with all results to display
	matrix `results_display'=(`Qtests' \ `dfQtests' \ `pQtests' \ `Ttests' \ `pTtests')
	matrix colnames `results_display' = Zero_Cond_ATE Const_Cond_ATE Zero_ATE
	matrix rownames `results_display' = Chi-Sq_Test dof_Chi-sq p-val_Chi-sq Norm_Test p-val_Norm
	
	* Display results
	di " "
	di as text "Zero Conditional ATE, Constant Conditional ATE and Zero ATE"
	di as text "`robtit'"
	di " "
	matrix list `results_display', noheader noblank format(%15.4f)
	
	* Return results
	return matrix Qtests = `Qtests'
	return matrix dfQtests = `dfQtests'
	return matrix pQtests = `pQtests'
	return matrix Ttests = `Ttests'
	return matrix pTtests = `pTtests'
	return local tests_order "Zero_Cond_ATE Constant_Cond_ATE Zero_ATE"
	return local variables_used `"`nocolvars'"'
end
